﻿using System;

using System.Windows.Input;
using Microsoft.Phone.Reactive;
namespace ReactiveUI.Xaml
{
    /// <summary>
    /// IReactiveCommand is an Rx-enabled version of ICommand that is also an
    /// Observable. Its Observable fires once for each invocation of
    /// ICommand.Execute and its value is the CommandParameter that was
    /// provided.
    /// </summary>
    public class ReactiveCommand : IReactiveCommand, ICommand, IObservable<object>,  IDisposable
    {
        private Func<object, bool> canExecuteExplicitFunc;
        protected ISubject<bool> canExecuteSubject;
        private IDisposable _inner;

        private IScheduler scheduler;
        private IObservable<object> executeObservable;
        public event EventHandler CanExecuteChanged;
        /// <summary>
        /// Fires whenever the CanExecute of the ICommand changes. 
        /// </summary>
        public IObservable<bool> CanExecuteObservable
        {
            get
            {
                return Observable.DistinctUntilChanged<bool>(this.canExecuteSubject);
            }
        }
        /// <summary>
        /// Creates a new ReactiveCommand object.
        /// </summary>
        /// <param name="canExecute">An Observable, often obtained via
        /// ObservableFromProperty, that defines when the Command can
        /// execute.</param>
        /// <param name="scheduler">The scheduler to publish events on - default
        /// is RxApp.DeferredScheduler.</param>
        public ReactiveCommand(IObservable<bool> canExecute = null, IScheduler scheduler = null)
        {

            executeObservable = Observable
            .FromEvent<EventArgs>(eh => OnExecute += eh, eh => OnExecute -= eh)
            .Select(e => e.Sender);
            canExecute = (canExecute ?? Observable.Concat<bool>(Observable.Return<bool>(true), Observable.Never<bool>()));
            this.commonCtor(scheduler);
            this._inner = ObservableExtensions.Subscribe<bool>(canExecute, new Action<bool>(this.canExecuteSubject.OnNext));


        }
        protected ReactiveCommand(Func<object, bool> canExecute, IScheduler scheduler = null)
        {
            this.canExecuteExplicitFunc = canExecute;
            this.commonCtor(scheduler);
        }
        /// <summary>
        /// Creates a new ReactiveCommand object in an imperative, non-Rx way,
        /// similar to RelayCommand.
        /// </summary>
        /// <param name="canExecute">A function that determines when the Command
        /// can execute.</param>
        /// <param name="executed">A method that will be invoked when the
        /// Execute method is invoked.</param>
        /// <param name="scheduler">The scheduler to publish events on - default
        /// is RxApp.DeferredScheduler.</param>
        /// <returns>A new ReactiveCommand object.</returns>
        public static ReactiveCommand Create(Func<object, bool> canExecute, Action<object> executed = null, IScheduler scheduler = null)
        {
            ReactiveCommand ret = new ReactiveCommand(canExecute, scheduler);
            if (executed != null)
            {
                ObservableExtensions.Subscribe<object>(ret, executed);
            }
            return ret;
        }
        private void commonCtor(IScheduler scheduler)
        {
            this.scheduler = (scheduler ?? Scheduler.CurrentThread);
            this.canExecuteSubject = new Subject<bool>();
            //this.canExecuteLatest = new ObservableAsPropertyHelper<bool>(this.canExecuteSubject, delegate(bool b)
            //{
            //    if (this.CanExecuteChanged != null)
            //    {
            //        this.CanExecuteChanged.Invoke(this, EventArgs.Empty);
            //    }
            //}
            //, true, scheduler);

            canExecuteSubject.Subscribe(
                o => 
                {
                    CanExecuteObservableLast = o;
                    if (this.CanExecuteChanged != null)
                    {
                        this.CanExecuteChanged.Invoke(this, EventArgs.Empty);
                    }
                }
                );


            

        }

        private event EventHandler<EventArgs> OnExecute =
            (o, e) => 
            { 
        
        };

        public virtual bool CanExecute(object parameter)
        {
            if (this.canExecuteExplicitFunc != null)
            {
                bool ret = this.canExecuteExplicitFunc.Invoke(parameter);
                this.canExecuteSubject.OnNext(ret);
                return ret;
            }
            return CanExecuteObservableLast;
        }

        bool CanExecuteObservableLast;

        public void Execute(object parameter)
        {

            OnExecute(parameter, new EventArgs());
        }
        public IDisposable Subscribe(IObserver<object> observer)
        {
            return this.executeObservable.Subscribe(observer);
        }
        public void Dispose()
        {
            if (this._inner != null)
            {
                this._inner.Dispose();
            }
        }
    }
}
